每個開發者在開發一套系統時,必定會開發些服務所需的相關元件,但如何方便建立這些元件又不佔用JVM的堆積(Heap)呢?就需要有一個容器暫存這些元件類別,此時,我們才選用Spring這套框架提供了多種注入方式,快速的從IoC配置池中進行注入開發者所需要的Bean元件,讓使用者成功地運用相關元件類別進行各類服務運算,在本章節中,我們提供了六種取得Bean類別元件方式,更佳的幫助讀者可透過這幾種方式可進一步獲得所需的解法。
無論使用註解(@Annotation)或XML方式獲取所需的Bean元件類別,如同前面章節所述,皆是透過BeanFactory進行獲取對應元件,由於本章節主要以註解方式進行注入元件,故以介紹@Autowired進行注入Bean元件原理為主,此方法主要透過AutowiredAnnotationBeanPostProcessor該類別進行實現,Spring主要採用CGLIB中的MethodInterceptor方法取得專案下的所有類別及註解,並將所有相關模式元件儲存於IoC配置池元件,我們稱此容器元件工廠為BeanFactory,當開發者配置@Autowired時,會自動注入相對應介面之元件或優先權(@Primary)最高之元件,若有多個繼承相同介面的元件及未有配置優先權(@Primary)之元件時,而開發者須配置預選元件名稱(@Qualifier),或透過ApplicationContext進行獲取Bean元件時給予對應預選元件名稱,由此可證,開發者所指定注入的物件,必須不可為共通性物件(Object),須為指定物件類別,透過此運作原理即可獲取您所配置的元件,相關範例如下。
相關Bean元件獲取方法
自動注入最高優先元件
@Primary
@Component("North")
public class NorthCityComponent implements AreaCityComponent {
@Override
public List<CityModel> getCityList() {
.....
.....
.....
}
}
@Autowired
AreaCityComponent primaryComponent;
透過@Qualifer配置預選元件名稱進行注入元件
@Component("Center")
public class CenterCityComponent implements AreaCityComponent {
@Override
public List<CityModel> getCityList() {
.....
.....
.....
}
}
@Qualifier("Center")
@Autowired
AreaCityComponent qualifierWithCenterAreaComponent;
透過@Qualifer配置預選元件名稱進行注入Override後的Bean元件
@Bean("SouthBean")
public SouthCityComponent getSouthCityComponent() {
return new SouthCityComponent() {
@Override
public List<CityModel> getCityList() {
List<CityModel> cityModels = southCityComponent.getCityList();
cityModels = cityModels.stream().map(cityModel -> {
if (cityModel.getName().equalsIgnoreCase("Penghu County")) {
cityModel.setSpecialLocalProduct("Brown Sugar Steamed Cake");
}
return cityModel;
}).collect(Collectors.toList());
return cityModels;
}
};
}
@Qualifier("SouthBean")
@Autowired
AreaCityComponent overrideWithSouthBeanComponent;
透過ApplicationContext取得Bean元件
AreaCityComponent eastCityComponent;
@Autowired
private ApplicationContext applicationContext;
@Before
public void init() {
eastCityComponent = applicationContext.getBean("East",AreaCityComponent.class);
........
}
透過BeanFactory取得Bean元件
AreaCityComponent fujianCityComponent;
@Autowired
BeanFactory beanFactory;
@Before
public void init() {
........
fujianCityComponent = beanFactory.getBean("Fujian",AreaCityComponent.class);
}
由上述範例我們可以看出,以@Autowired方式經由多層代理進行注入最方便,但透過BeanFactory直接進行注入取得是最快速的方式,供各位開發者作參考及引用。
由此架構圖我們可得知開發者所運用的注入式註解(@Autowired)皆透過SpringBeanAutowiringSupport此類別進行代理產生,其架構流程如圖一所示,所有注入式註解(@Autowired)標籤皆透過AutowiredAnnotationBeanPostProcessor核心類別元件進行產生出InjectionMetadata物件,並觸發inject方法產生出對應開發者所需之元件類別,在這過程中必須經過三道邏輯演算法,第一道流程processInjection,區分為方法類型註解(Method Annotation)及欄位類型註解(Field Annotation),此流程會判斷所待處理支援間類別是否有配置候選元件名稱註解(@Qualifier),有混合型註解配置,會觸發postProcessMergedBeanDefinition方法,若其註解作用在方法上,最終會觸發postProcessProperties方法,若僅為單一註解(@Autowired),則進行觸發processInjection方法,所有元資料(Metadata)配置完後即傳送至第二道流程findAutowiringMetadata,若有則選此名稱作為Bean Name,若無則以類別名稱當做Bean Name,並取得相關快取資料後進行相關邏輯處理,最後進入第三方法buildAutowiringMetadata,此方法會分為欄位型及方法型反射性處理,最後透過ShortcutDependencyDesriptor此類別進行代理產生InjectionMetadata物件處理相關反射元件行為。
圖一、@Autowird 注入架構圖
Run test task
gradle test
Run open result html
open ./build/reports/tests/test/index.html
測試結果,透過ApplicationContext及@Autowired皆達到預期目標
How to Get Application Context in Spring Boot
String Boot 使用BeanFactory動態取得bean BeanFactory get bean dynamically
Spring Source Analysis: @Autowire Annotation Principle Analysis
Constructor Dependency Injection in Spring
The Spring @Qualifier Annotation